/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package informationtheoryproject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Scanner;

import java.util.TreeMap;
import util.Huffman;

/**
 * 
 * @author keldjan
 */
public class ITUtil {

    public static List probabilidades;
    public static double tamanho = 0;
    public static TreeMap<String, Double> hm;

    // Quantidades
    public static double qtdeA = 0;
    public static double qtdeB = 0;
    public static double qtdeC = 0;
    public static double qtdeD = 0;
    public static double qtdeE = 0;
    public static double qtdeF = 0;
    public static double qtdeG = 0;
    public static double qtdeH = 0;
    public static double qtdeI = 0;
    public static double qtdeJ = 0;
    public static double qtdeK = 0;
    public static double qtdeL = 0;
    public static double qtdeM = 0;
    public static double qtdeN = 0;
    public static double qtdeO = 0;
    public static double qtdeP = 0;
    public static double qtdeQ = 0;
    public static double qtdeR = 0;
    public static double qtdeS = 0;
    public static double qtdeT = 0;
    public static double qtdeU = 0;
    public static double qtdeV = 0;
    public static double qtdeW = 0;
    public static double qtdeX = 0;
    public static double qtdeY = 0;
    public static double qtdeZ = 0;
    public static double qtdeSpace = 0;

    public StringBuilder readFile(String fileName) {

        File lendo = new File("" + fileName);
        String s = null;

        StringBuilder sb2 = new StringBuilder();

        try {
            FileReader fr = new FileReader(lendo);
            BufferedReader br = new BufferedReader(fr);
            while ((s = br.readLine()) != null) {
                StringBuilder sb = new StringBuilder();
                sb.append(s.toLowerCase(new Locale("pt", "BR")));
                for (int i = 0; i < sb.length(); i++) {

                    char caracter = sb.charAt(i);
                    // Remove a acentuacao
                    if (caracter != 'a' && caracter != 'b' && caracter != 'c' && caracter != 'd' && caracter != 'e' && caracter != 'f' && caracter != 'g' && caracter != 'h' && caracter != 'i' && caracter != 'j' && caracter != 'k' && caracter != 'l' && caracter != 'm' && caracter != 'n' && caracter != 'o' && caracter != 'p' && caracter != 'q' && caracter != 'r' && caracter != 's' && caracter != 't' && caracter != 'u' && caracter != 'v' && caracter != 'w' && caracter != 'x' && caracter != 'y' && caracter != 'z' && caracter != ' ') {
                        if (caracter == 'á' || caracter == 'ã' || caracter == 'â' || caracter == 'à' || caracter == 'ä') {
                            sb.setCharAt(i, 'a');
                            qtdeA += 1;
                            tamanho = tamanho + 1;
                        } else {
                            if (caracter == 'é' || caracter == 'è' || caracter == 'ê' || caracter == 'ë') {
                                sb.setCharAt(i, 'e');
                                qtdeE += 1;
                                tamanho = tamanho + 1;
                            } else {
                                if (caracter == 'í' || caracter == 'ì' || caracter == 'î' || caracter == 'ï') {
                                    sb.setCharAt(i, 'i');
                                    qtdeI += 1;
                                    tamanho = tamanho + 1;
                                } else {
                                    if (caracter == 'ó' || caracter == 'ò' || caracter == 'õ' || caracter == 'ô' || caracter == 'ö') {
                                        sb.setCharAt(i, 'o');
                                        qtdeO += 1;
                                        tamanho = tamanho + 1;
                                    } else {
                                        if (caracter == 'ú' || caracter == 'ù' || caracter == 'û' || caracter == 'ü') {
                                            sb.setCharAt(i, 'u');
                                            qtdeU += 1;
                                            tamanho = tamanho + 1;
                                        } else {
                                            if (caracter == 'ç') {
                                                sb.setCharAt(i, 'c');
                                            } else {
                                                // Remocao da pontuacao
                                                switch (caracter) {
                                                    case '!':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '\"':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '\'':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '.':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '?':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case ';':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '&':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '*':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '=':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '#':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '|':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '+':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '-':
                                                        sb.setCharAt(i, ' ');
                                                        qtdeSpace += 1;
                                                        tamanho = tamanho + 1;
                                                        break;
                                                    case '\t':
                                                        sb.setCharAt(i, ' ');
                                                        qtdeSpace += 1;
                                                        tamanho = tamanho + 1;
                                                        break;
                                                    case ',':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '(':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case ')':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '@':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case 'ª':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case 'º':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case ':':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '[':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case ']':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '{':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '}':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '$':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '%':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '0':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '1':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '2':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '3':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '4':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '5':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '6':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '7':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '8':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    case '9':
                                                        sb.deleteCharAt(i);
                                                        i--;
                                                        break;
                                                    default:
                                                        break;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        // Contando caracteres
                        switch (caracter) {
                            case 'a':
                                qtdeA += 1;
                                break;
                            case 'b':
                                qtdeB += 1;
                                break;
                            case 'c':
                                qtdeC += 1;
                                break;
                            case 'd':
                                qtdeD += 1;
                                break;
                            case 'e':
                                qtdeE += 1;
                                break;
                            case 'f':
                                qtdeF += 1;
                                break;
                            case 'g':
                                qtdeG += 1;
                                break;
                            case 'h':
                                qtdeH += 1;
                                break;
                            case 'i':
                                qtdeI += 1;
                                break;
                            case 'j':
                                qtdeJ += 1;
                                break;
                            case 'k':
                                qtdeK += 1;
                                break;
                            case 'l':
                                qtdeL += 1;
                                break;
                            case 'm':
                                qtdeM += 1;
                                break;
                            case 'n':
                                qtdeN += 1;
                                break;
                            case 'o':
                                qtdeO += 1;
                                break;
                            case 'p':
                                qtdeP += 1;
                                break;
                            case 'q':
                                qtdeQ += 1;
                                break;
                            case 'r':
                                qtdeR += 1;
                                break;
                            case 's':
                                qtdeS += 1;
                                break;
                            case 't':
                                qtdeT += 1;
                                break;
                            case 'u':
                                qtdeU += 1;
                                break;
                            case 'v':
                                qtdeV += 1;
                                break;
                            case 'w':
                                qtdeW += 1;
                                break;
                            case 'x':
                                qtdeX += 1;
                                break;
                            case 'y':
                                qtdeY += 1;
                                break;
                            case 'z':
                                qtdeZ += 1;
                                break;
                            case ' ':
                                qtdeSpace += 1;
                                break;
                        }
                        tamanho = tamanho + 1;
                    }
                }
                sb2.append(sb.toString());
            }

            hm = new TreeMap<String, Double>();
            hm.put(" ", Double.valueOf(qtdeSpace));
            hm.put("A", Double.valueOf(qtdeA));
            hm.put("B", Double.valueOf(qtdeB));
            hm.put("C", Double.valueOf(qtdeC));
            hm.put("D", Double.valueOf(qtdeD));
            hm.put("E", Double.valueOf(qtdeE));
            hm.put("F", Double.valueOf(qtdeF));
            hm.put("G", Double.valueOf(qtdeG));
            hm.put("H", Double.valueOf(qtdeH));
            hm.put("I", Double.valueOf(qtdeI));
            hm.put("J", Double.valueOf(qtdeJ));
            hm.put("K", Double.valueOf(qtdeK));
            hm.put("L", Double.valueOf(qtdeL));
            hm.put("M", Double.valueOf(qtdeM));
            hm.put("N", Double.valueOf(qtdeN));
            hm.put("O", Double.valueOf(qtdeO));
            hm.put("P", Double.valueOf(qtdeP));
            hm.put("Q", Double.valueOf(qtdeQ));
            hm.put("R", Double.valueOf(qtdeR));
            hm.put("S", Double.valueOf(qtdeS));
            hm.put("T", Double.valueOf(qtdeT));
            hm.put("U", Double.valueOf(qtdeU));
            hm.put("V", Double.valueOf(qtdeV));
            hm.put("W", Double.valueOf(qtdeW));
            hm.put("X", Double.valueOf(qtdeX));
            hm.put("Y", Double.valueOf(qtdeY));
            hm.put("Z", Double.valueOf(qtdeZ));

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return sb2;
    }

    /*
     * Esse metodo coloca as probabilidades num Arralist para que o metodo
     * Entropy possa utiliza 
     */
    public void probabilities() {
        //
        double soma = 0.0;
        probabilidades = new ArrayList();
        for (Iterator it = hm.values().iterator(); it.hasNext();) {
            Double element = (Double) it.next();
            probabilidades.add(element / tamanho);
            soma = soma + (element / tamanho);
        }
        System.out.println("Somatorio das probs: " + (soma));
    }

    /**
     * Esse metodo calcula a entropia
     */
    public double entropy() {
        double entropy = 0.0;

        for (Iterator it = probabilidades.iterator(); it.hasNext();) {
            Double element = (Double) it.next();
            entropy = entropy + (element.doubleValue() * (Math.log(1.0 / element.doubleValue())) / Math.log(2));
        }

        return entropy;
    }

    /*
     * Esse metodo calcula o comprimento medio
     */
    public double meanLength(List<Double> lengths) {
        double meanLength = 0.0;
        for (Iterator it = lengths.iterator(), it2 = probabilidades.iterator(); it.hasNext() && it2.hasNext();) {
            Double lenght = (Double) it.next();
            Double prob = (Double) it2.next();
            meanLength = meanLength + (lenght * prob);
        }

        return meanLength;
    }

    public static void main(String args[]) {
        
        System.out.println("====================================================================");
        System.out.println("===========Projeto de Comunicacao Digital II========================");
        System.out.println("====== Equipe: Keldjan Alves e Hilda Borborema =====================");
        System.out.println("====================================================================");
        ITUtil it = new ITUtil();
        System.out.print("Digite o nome do arquivo a ser usado: ");
        Scanner input = new Scanner(System.in);
        String nomeArquivo = input.nextLine();
        StringBuilder st = it.readFile(nomeArquivo);

        /* 
         * Esse for eh responsavel por mostrar cada simbolo e suas respectivas
         * probabilidades
         */
        for (Iterator it2 = hm.values().iterator(), it3 = hm.keySet().iterator(); it2.hasNext() && it3.hasNext();) {
            Double element1 = (Double) it2.next();
            String element2 = (String) it3.next();
            System.out.println("Simbolo \t Probabilidade");
            System.out.println(element2 + "\t\t" + (element1 / tamanho));
        }

        double X = tamanho * 7;
        System.out.println("TAMANHO X: " + X);

        /* 
         * Esse metodo probabilities() cria um List de probabilidades que serah usado
         * pelo metodo entropy()
         */
        it.probabilities();
        double entropy = it.entropy();
        System.out.println("Entropia: " + entropy);

        /* 
         * Esse toString() no codigo Huffman eh responsavel
         * pelo print da tabela de Huffman
         */
        Huffman hf = new Huffman(st.toString());
        System.out.println(hf.toString());
        double comprimentoMedio = it.meanLength(hf.getComprimentos());
        System.out.println("Comprimento medio: " + comprimentoMedio);

        /*
         * Esse for estah modificando o arquivo original.
         * Nele, todas a letras estaum sendo subtituidas pelo
         * seu respectivo codigo na codificacao Huffman 
         */
        for (Iterator it4 = Huffman.codes.values().iterator(), it5 = Huffman.codes.keySet().iterator(); it4.hasNext() && it5.hasNext();) {
            String srr = (String) it4.next();
            String ot = (String) it5.next();
            System.out.println("Modificando " + srr + "...");

            char caractere;

            for (int i = 0; i < st.length(); i++) {
                caractere = st.charAt(i);
                if (caractere == srr.charAt(0)) {
                    st = st.delete(i, i + 1);
                    st = st.insert(i, ot);
                }
            }
        }

        /*
         * Y =  Tamanho total do arquivo modificado 
         */
        double Y = st.length();
        double eta = entropy / comprimentoMedio;
        System.out.println("EFICIENCIA: " + eta);
        System.out.println("REDUNDANCIA: " + (1 - eta));
        System.out.println("TAMANHO Y: " + Y);
        System.out.println("TAXA de COMPRESSAO X / Y: " + X / Y);

        // Se quiser ver o texto modificado, eh soh descomentar a linha abaixo
        //System.out.println("MODIFICADO: " + st);

        /*
         * Nessa parte , estah sendo calculado a porcentagem de zeros e uns no texto modificado
         */
        double zero = 0;
        double um = 0;
        for (int j = 0; j < st.length(); j++) {
            char c = st.charAt(j);
            if (c == '0') {
                zero += 1;
            } else {
                um += 1;
            }
        }
        double espacoAmostral = zero + um;
        System.out.println("Prob. de Zero: " + (zero / espacoAmostral));
        System.out.println("Prob. de Um: " + (um / espacoAmostral));

        /*
         * Essa parte é responsavel pela persistencia do texto modificado em um
         * arquivo txt, porem para evitar uma linha apenas no arquivo, foram 
         * concatenados varios \n para pular linha.
         */
        try {
            // Persistindo em arquivo
            File file = new File(nomeArquivo + "_modificado.txt");
            for (int i = 30; i < st.length(); i = i + 30) {
                st.insert(i, "\n");
            }

            PrintWriter pw = new PrintWriter(file);
            pw.write(st.toString());
            pw.flush();
            pw.close();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
